package chromosomes;

import logic.Tree;
import logic.TypeMutation;

public class Chromosome {

	private Tree tree;
	private double fitness;
	private double punt;
	private double puntAccumulated;

	public Chromosome() {
		setTree(new Tree(2, 5, null));
		evaluate();
	}

	public Chromosome(int depth) {
		setTree(new Tree(depth, depth, null));
		evaluate();
	}

	public Chromosome(int minDepth, int maxDepth) {
		setTree(new Tree(minDepth, maxDepth, null));
		evaluate();
	}

	public Chromosome(Tree tree) {
		setTree((Tree) tree.clone());
		evaluate();
	}

	public Chromosome(Tree tree, double fitness, double punt,
			double puntAccumulated) {
		setFitness(fitness);
		setpSelection(punt);
		setpAccumulated(puntAccumulated);
		setTree(tree);
	}

	public void evaluate() {
		int corrects = 0;
		boolean[] d;
		boolean[] a;
		switch (GeneticAlgorithm.getMux()) {
		case SIX:
			d = new boolean[4];
			a = new boolean[2];
			break;
		default:
			d = new boolean[8];
			a = new boolean[3];
			break;
		}
		int max = (int) Math.pow(2, d.length + a.length) - 1;
		for (int i = 0; i < max; i++) {
			plus1(a, d);
			if (optimum(a, d) == tree.evaluate(a, d)) {
				corrects++;
			}
		}
		fitness = max - corrects;
	}

	public double getFitness() {
		return fitness;
	}

	public int getNumNodes() {
		return tree.getNumNodes();
	}

	public double getpAccumulated() {
		return puntAccumulated;
	}

	public double getpSelection() {
		return punt;
	}

	public Tree getTree() {
		return tree;
	}

	public void mutate(double p, TypeMutation typeMutation) {
		tree.mutate(p, typeMutation);
	}

	public void setFitness(double fitness) {
		this.fitness = fitness;
	}

	public void setpAccumulated(double p) {
		puntAccumulated = p;
	}

	public void setpSelection(double p) {
		punt = p;
	}

	public void setTree(Tree tree) {
		this.tree = tree;
	}

	@Override
	public String toString() {
		String t = tree.toString();
		return "Fitness: " + fitness + "\nNum nodes: " + tree.getNumNodes()
				+ "\n" + t.substring(1, t.length());
	}

	@Override
	protected Object clone() {
		return new Chromosome((Tree) tree.clone(), fitness, punt,
				puntAccumulated);
	}

	private boolean optimum(boolean[] as, boolean[] ds) {
		String binary = "";
		for (int i = as.length - 1; i >= 0; i--) {
			binary += as[i] == true ? "1" : "0";
		}
		int index = Integer.parseInt(binary, 2);
		return ds[index];
	}

	private void plus1(boolean[] as, boolean[] ds) {
		boolean carry = true;
		for (int i = 0; i < ds.length; i++) {
			boolean aux = carry && ds[i];
			ds[i] = carry ^ ds[i];
			carry = aux;
		}
		for (int i = 0; i < as.length; i++) {
			boolean aux = carry && as[i];
			as[i] = carry ^ as[i];
			carry = aux;
		}
	}

}
